﻿using System;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.IdentityModel.Tokens;

namespace RelyingPartyService
{
	#region RPConfiguration
	public class RpConfiguration : ConfigurationSection
	{
		const string SectionName = "rpConfiguration";
		static RpConfiguration _configuration = null;

		public static RpConfiguration Current
		{
			get
			{
				if (_configuration == null)
				{
					_configuration = (RpConfiguration)ConfigurationManager.GetSection(RpConfiguration.SectionName);
					if (_configuration == null)
						throw new ConfigurationErrorsException("Configuration RP Section not found!");
				}
				return _configuration;
			}
		}

		[ConfigurationProperty("stsTokens")]
		public ServiceCertificateCollection StsTokens
		{
			get { return (ServiceCertificateCollection)this["stsTokens"]; }
		}

		[ConfigurationProperty("rpTokens")]
		public ServiceCertificateCollection RpTokens
		{
			get { return (ServiceCertificateCollection)this["rpTokens"]; }
		}

		public SecurityToken GetRpToken(EndpointAddress rp)
		{
			if (rp == null)
				throw new ArgumentNullException("issuer");

			if (!RpTokens.Contains(rp))
				return null;

			return GetCertificate(RpTokens.Item(rp));
		}

		public SecurityToken GetStsToken(EndpointAddress issuer)
		{
			if (issuer == null)
				throw new ArgumentNullException("issuer");

			if (!StsTokens.Contains(issuer))
				return null;

			return GetCertificate(StsTokens.Item(issuer));
		}

		private X509SecurityToken GetCertificate(ServiceCertificateConfiguration certificate)
		{
			X509Store store = new X509Store(certificate.StoreName, certificate.StoreLocation);
			X509SecurityToken token = null;
			try
			{
				store.Open(OpenFlags.OpenExistingOnly);
				X509Certificate2Collection collection = store.Certificates.Find(certificate.FindType, certificate.FindValue, true);
				if (collection.Count == 0)
					throw new Exception(certificate.FindValue + " Certificate not found");
				token = new X509SecurityToken(collection[0]);
			}
			finally
			{
				store.Close();
			}
			return token;
		}

	}
	#endregion

	#region ServiceCertificateCollection
	[ConfigurationCollection(typeof(ServiceCertificateConfiguration))]
	public class ServiceCertificateCollection : ConfigurationElementCollection
	{
		protected override ConfigurationElement CreateNewElement()
		{
			return new ServiceCertificateConfiguration();
		}

		protected override Object GetElementKey(ConfigurationElement element)
		{
			return new EndpointAddress(((ServiceCertificateConfiguration)element).Uri.ToString());
		}

		internal bool Contains(EndpointAddress endpoint)
		{
			return Item(endpoint) != null;
		}

		internal ServiceCertificateConfiguration Item(EndpointAddress endpoint)
		{
			foreach (ServiceCertificateConfiguration scc in this)
			{
				if (scc.Uri == endpoint.Uri)
					return scc;
			}
			return null;
		}
	}
	#endregion

	#region ServiceCertificateConfiguration

	public class ServiceCertificateConfiguration : ConfigurationElement
	{
		public ServiceCertificateConfiguration() { }

		[ConfigurationProperty("uri", IsRequired = true, IsKey = true)]
		public Uri Uri
		{
			get { return (Uri)this["uri"]; }
		}

		[ConfigurationProperty("storeLocation", IsRequired = true, DefaultValue = StoreLocation.LocalMachine)]
		public StoreLocation StoreLocation
		{
			get { return (StoreLocation)this["storeLocation"]; }
		}

		[ConfigurationProperty("storeName", IsRequired = true, DefaultValue = StoreName.My)]
		public StoreName StoreName
		{
			get { return (StoreName)this["storeName"]; }
		}

		[ConfigurationProperty("findValue", IsRequired = true)]
		public string FindValue
		{
			get { return (string)this["findValue"]; }
		}

		[ConfigurationProperty("findType", IsRequired = true, DefaultValue = X509FindType.FindBySubjectDistinguishedName)]
		public X509FindType FindType
		{
			get { return (X509FindType)this["findType"]; }
		}
	}
	#endregion

}
